﻿#include "NPCObject.h"
#include "../MirScenes/GameScene.h"
#include "../MirGraphics/MLibrary.h"
#include "../Forms/Client.CMain.h"
#include "../../Shared/Functions/Functions.h"
#include "PlayerObject.h"
#include "../MirControls/MirLabel.h"

using namespace Client::MirControls;
using namespace Client::MirGraphics;
using namespace Client::MirScenes;
namespace S = ServerPackets;

namespace Client::MirObjects
{

    ObjectType NPCObject::getRace() const
    {
        return ObjectType::Merchant;
    }

    bool NPCObject::getBlocking() const
    {
        return true;
    }

    bool NPCObject::getCanChangeDir() const
    {
        return _canChangeDir;
    }

    void NPCObject::setCanChangeDir(bool value)
    {
        _canChangeDir = value;
        if (value == false)
        {
            Direction = (MirDirection)0;
        }
    }

    NPCObject::NPCObject(unsigned int objectID) : MapObject(objectID)
    {
    }

    void NPCObject::Load(S::ObjectNPC *info)
    {
        Name = info->Name;
        NameColour = info->NameColour;
        CurrentLocation = info->Location;
        Direction = info->Direction;
        Movement = info->Location;
        MapLocation = info->Location;
        GameScene::Scene->MapControl->AddObject(this);

        Quests = GameScene::QuestInfoList.Where([&] (std::any c)
        {
            return c->NPCIndex == ObjectID;
        }).ToList();

        Image = info->Image;
        Colour = info->Colour;

        LoadLibrary();

        Frames = (!BodyLibrary->getFrames()->empty()) ? BodyLibrary->getFrames() : FrameSet::DefaultNPC;

        Light = 10;
        BaseIndex = 0;

        SetAction();
    }

    void NPCObject::LoadLibrary()
    {
        if (Image < Libraries::NPCs.size())
        {
            BodyLibrary = Libraries::NPCs[Image];
        }
        else if (Image >= 1000 && Image < 1100)
        {
            BodyLibrary = Libraries::Flags[Image - 1000];
        }
    }

    void NPCObject::Process()
    {
        bool update = CMain::Time >= NextMotion || GameScene::CanMove;

        ProcessFrames();

        if (update)
        {
            UpdateBestQuestIcon();
        }

        if (Frame == nullptr)
        {
            DrawFrame = 0;
            DrawWingFrame = 0;
        }
        else
        {
            DrawFrame = Frame->Start + (Frame->getOffSet() * static_cast<unsigned char>(Direction)) + FrameIndex;
            DrawWingFrame = Frame->EffectStart + (Frame->getEffectOffSet() * static_cast<unsigned char>(Direction)) + EffectFrameIndex;
        }

        DrawY = CurrentLocation->Y;

        DrawLocation = new Point((Movement->X - User->Movement->X + MapControl::OffSetX) * MapControl::CellWidth, (Movement->Y - User->Movement->Y + MapControl::OffSetY) * MapControl::CellHeight);
        DrawLocation->Offset(User->OffSetMove);
        DrawLocation->Offset(getGlobalDisplayLocationOffset());

        if (BodyLibrary != nullptr)
        {
            FinalDrawLocation = Functions::Add(DrawLocation, BodyLibrary->GetOffSet(DrawFrame));
        }

        if (BodyLibrary != nullptr && update)
        {
            FinalDrawLocation = Functions::Add(DrawLocation, BodyLibrary->GetOffSet(DrawFrame));
            DisplayRectangle = new Rectangle(DrawLocation, BodyLibrary->GetTrueSize(DrawFrame));
        }

        for (int i = 0; i < Effects.size(); i++)
        {
            Effects[i]->Process();
        }

        Color *colour = DrawColour;

        switch (Poison)
        {
            case PoisonType::None:
                DrawColour = Color::White;
                break;
            case PoisonType::Green:
                DrawColour = Color::Green;
                break;
            case PoisonType::Red:
                DrawColour = Color::Red;
                break;
            case PoisonType::Bleeding:
                DrawColour = Color::DarkRed;
                break;
            case PoisonType::Slow:
                DrawColour = Color::Purple;
                break;
            case PoisonType::Stun:
            case PoisonType::Dazed:
                DrawColour = Color::Yellow;
                break;
            case PoisonType::Blindness:
                DrawColour = Color::MediumVioletRed;
                break;
            case PoisonType::Frozen:
                DrawColour = Color::Blue;
                break;
            case PoisonType::Paralysis:
            case PoisonType::LRParalysis:
                DrawColour = Color::Gray;
                break;
        }


        if (colour != DrawColour)
        {
            GameScene::Scene->MapControl->TextureValid = false;
        }


        if (CMain::Time > QuestTime)
        {
            QuestTime = CMain::Time + 500;
            if (++QuestIndex > 1)
            {
                QuestIndex = 0;
            }
        }
    }

    void NPCObject::ProcessFrames()
    {
        if (Frame == nullptr)
        {
            return;
        }

        switch (CurrentAction)
        {
            case MirAction::Standing:
            case MirAction::Harvest:
                if (CMain::Time >= NextMotion)
                {
                    GameScene::Scene->MapControl->TextureValid = false;

                    if (SkipFrames)
                    {
                        UpdateFrame();
                    }

                    if (UpdateFrame() >= Frame->Count)
                    {
                        FrameIndex = Frame->Count - 1;
                        SetAction();
                    }
                    else
                    {
                        NextMotion += FrameInterval;
                    }
                }

                if (EffectFrameInterval > 0)
                {
                if (CMain::Time >= NextMotion2)
                {
                    GameScene::Scene->MapControl->TextureValid = false;

                    if (SkipFrames)
                    {
                        UpdateFrame2();
                    }

                    if (UpdateFrame2() >= Frame->EffectCount)
                    {
                        EffectFrameIndex = Frame->EffectCount - 1;
                    }
                    else
                    {
                        NextMotion2 += EffectFrameInterval;
                    }
                }
                }
                break;

        }

    }

    int NPCObject::UpdateFrame()
    {
        if (Frame == nullptr)
        {
            return 0;
        }

        if (Frame->Reverse)
        {
            return std::abs(--FrameIndex);
        }

        return ++FrameIndex;
    }

    int NPCObject::UpdateFrame2()
    {
        if (Frame == nullptr)
        {
            return 0;
        }

        if (Frame->Reverse)
        {
            return std::abs(--EffectFrameIndex);
        }

        return ++EffectFrameIndex;
    }

    void NPCObject::SetAction()
    {
        if (ActionFeed.empty())
        {
            if (CMain::Random->Next(2) == 0 && Frames->size() > 1)
            {
                CurrentAction = MirAction::Harvest;
            }
            else
            {
                CurrentAction = MirAction::Standing;
            }

            std::unordered_map::const_iterator Frames_iterator = Frames->find(CurrentAction);
            Frame = Frames_iterator->second;
            FrameIndex = 0;
            EffectFrameIndex = 0;

            if (MapLocation != CurrentLocation)
            {
                GameScene::Scene->MapControl->RemoveObject(this);
                MapLocation = CurrentLocation;
                GameScene::Scene->MapControl->AddObject(this);
            }

            if (Frame == nullptr)
            {
                return;
            }

            FrameInterval = Frame->Interval;
            EffectFrameInterval = Frame->EffectInterval;
        }
        else
        {
            QueuedAction *action = ActionFeed[0];
            ActionFeed.erase(ActionFeed.begin());

            CurrentAction = action->Action;
            CurrentLocation = action->Location;

            //if(CanChangeDir)
            //    Direction = action.Direction;

            FrameIndex = 0;
            EffectFrameIndex = 0;

            if (Frame == nullptr)
            {
                return;
            }

            FrameInterval = Frame->Interval;
            EffectFrameInterval = Frame->EffectInterval;
        }

        NextMotion = CMain::Time + FrameInterval;
        NextMotion2 = CMain::Time + EffectFrameInterval;

        GameScene::Scene->MapControl->TextureValid = false;

    }

    void NPCObject::Draw()
    {
        if (BodyLibrary == nullptr)
        {
            return;
        }

        //BodyLibrary.Draw(DrawFrame, DrawLocation, DrawColour, true);

        BodyLibrary->DrawTinted(DrawFrame, DrawLocation, DrawColour, Colour, true);

        if (QuestIcon == QuestIcon::None)
        {
            return;
        }

        auto offSet = BodyLibrary->GetOffSet(BaseIndex);
        auto size = BodyLibrary->GetSize(BaseIndex);

        int imageIndex = 981 + (static_cast<int>(QuestIcon) * 2) + QuestIndex;

        Libraries::Prguse->Draw(imageIndex, Functions::Add(Functions::Add(DrawLocation, offSet), size->Width / 2 - 28, -40), Color::White, false);
    }

    bool NPCObject::MouseOver(Point *p)
    {
        return MapControl::getMapLocation() == CurrentLocation || BodyLibrary != nullptr && BodyLibrary->VisiblePixel(DrawFrame, Functions::Subtract(p, FinalDrawLocation), false);
    }

    void NPCObject::DrawBehindEffects(bool effectsEnabled)
    {
    }

    void NPCObject::DrawEffects(bool effectsEnabled)
    {
        if (!effectsEnabled)
        {
            return;
        }

        if (BodyLibrary == nullptr)
        {
            return;
        }

        if (DrawWingFrame > 0)
        {
            BodyLibrary->DrawBlend(DrawWingFrame, DrawLocation, Color::White, true);
        }
    }

    void NPCObject::DrawName()
    {
        if (!Name.find("_") != std::string::npos)
        {
            MapObject::DrawName();
            return;
        }

        std::vector<std::string> splitName = StringHelper::split(Name, '_');

        for (int s = 0; s < splitName.Count(); s++)
        {
            CreateNPCLabel(splitName[s], s);

            TempLabel->setText(splitName[s]);
            Point tempVar(DisplayRectangle->X + (48 - TempLabel->getSize()->Width) / 2, DisplayRectangle->Y - (32 - TempLabel->getSize()->Height / 2) + (Dead ? 35 : 8) - (((splitName.Count() - 1) * 10) / 2) + (s * 12));
            TempLabel->setLocation(&tempVar);
            TempLabel->Draw();
        }
    }

    void NPCObject::CreateNPCLabel(const std::string &word, int wordOrder)
    {
        TempLabel = nullptr;

        for (int i = 0; i < LabelList.size(); i++)
        {
            if (LabelList[i]->getText() != word || LabelList[i]->getForeColour() != (wordOrder == 0 ? NameColour : Color::White))
            {
                continue;
            }
            TempLabel = LabelList[i];
            break;
        }

        if (TempLabel != nullptr && !TempLabel->isDisposed())
        {
            return;
        }

        TempLabel = new MirLabel();
        TempLabel->setAutoSize(true);
        TempLabel->setBackColour(Color::Transparent);
        TempLabel->setForeColour(wordOrder == 0 ? NameColour : Color::White);
        TempLabel->setOutLine(true);
        TempLabel->setOutLineColour(Color::Black);
        TempLabel->setText(word);

        TempLabel->Disposing->addListener([&] (o, e)
        {
            return LabelList.Remove(TempLabel);
        });
        LabelList.push_back(TempLabel);
    }

    void NPCObject::UpdateBestQuestIcon()
    {
        ClientQuestProgress *quests = GetAvailableQuests(true).FirstOrDefault();
        QuestIcon bestIcon = QuestIcon::None;

        if (quests != nullptr)
        {
            bestIcon = quests->getIcon();
        }

        QuestIcon = bestIcon;
    }

    std::vector<ClientQuestProgress*> NPCObject::GetAvailableQuests(bool returnFirst)
    {
        std::vector<ClientQuestProgress*> quests;

        for (ClientQuestProgress *q : *User->CurrentQuests.Where([&] (std::any q)
        {
            return !std::find(User->CompletedQuests.begin(), User->CompletedQuests.end(), q->QuestInfo->Index) != User->CompletedQuests.end();
        }))
        {
            if (q::QuestInfo->FinishNPCIndex == ObjectID)
            {
                quests.push_back(q);
            }
            else if (q::QuestInfo->NPCIndex == ObjectID && q::QuestInfo::SameFinishNPC)
            {
                quests.push_back(q);

                if (returnFirst)
                {
                    return quests;
                }
            }
        }

        for (ClientQuestProgress *quest : (from q in Quests where!std::find_if(quests.begin(), quests.end(), [&] (std::any p)
        {
            return p::QuestInfo->Index == q::Index;
        }) != quests.end() where CanAccept(q) where!std::find(User->CompletedQuests.begin(), User->CompletedQuests.end(), q::Index) != User->CompletedQuests.end() select q)->Select([&] (std::any q)
        {
            ClientQuestProgress *tempVar = new ClientQuestProgress();
            tempVar->QuestInfo = q;
            tempVar->Taken = true;
            tempVar->Completed = false;
            ClientQuestProgress *tempVar2 = new ClientQuestProgress();
            tempVar2->QuestInfo = q;
            std::find_if(User->CurrentQuests.begin(), User->CurrentQuests.end(), [&] (std::any p)
            {
                return p::QuestInfo->Index == q::Index;
            }) != User->CurrentQuests.end() ? tempVar : tempVar2;

            delete tempVar2;
            delete tempVar;
        }))
        {

            if (returnFirst)
            {
                return quests;
            }
        }
    }

    bool <missing_class_definition>::CanAccept(ClientQuestInfo *quest)
    {
        if (quest->MinLevelNeeded > User->Level || quest->MaxLevelNeeded < User->Level)
        {
            return false;
        }

        if (!quest->ClassNeeded::HasFlag(RequiredClass::None))
        {
            switch (User->Class)
            {
                case MirClass::Warrior:
                    if (!quest->ClassNeeded::HasFlag(RequiredClass::Warrior))
                    {
                        return false;
                    }
                    break;
                case MirClass::Wizard:
                    if (!quest->ClassNeeded::HasFlag(RequiredClass::Wizard))
                    {
                        return false;
                    }
                    break;
                case MirClass::Taoist:
                    if (!quest->ClassNeeded::HasFlag(RequiredClass::Taoist))
                    {
                        return false;
                    }
                    break;
                case MirClass::Assassin:
                    if (!quest->ClassNeeded::HasFlag(RequiredClass::Assassin))
                    {
                        return false;
                    }
                    break;
                case MirClass::Archer:
                    if (!quest->ClassNeeded::HasFlag(RequiredClass::Archer))
                    {
                        return false;
                    }
                    break;
            }
        }

        //check against active quest list
        return quest->QuestNeeded <= 0 || User->CompletedQuests->Contains(quest->QuestNeeded);
    }
}
